﻿using HXDNB.DAL;
using HXDNB.Infrastructure;
using HXDNB.Infrastructure.Extension;
using HXDNB.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using System.Xml.Linq;
using h = HXDNB.Infrastructure.Helper;

namespace HXDNB.CMS.Controllers
{
    public class PublishController : BaseController
    {
        //
        // GET: /Publish/

        public ActionResult Index(string type)
        {
            ViewBag.Type = type;
            return View();
        }

        /// <summary>
        /// 整体生成
        /// </summary>
        public ActionResult All()
        {
            try
            {
                //部署所有文章
                using (var ab = new ArticleBLL())
                {
                    ReleaseArticle(ab.GetAllArticles());

                    ReleaseWebIndex();
                }

                //部署广告文件(js&image)
                CreateAd(true, false);
                CreateAd(false, false);

                //压缩打包待发布文件
                var zipFile = h.Zip.ZipDir(Config.Release)
                                    ? Url.Content(string.Format(@"~/Release/{0}v{1}.zip", DateTime.Today.ToString("yyyyMMdd"), Config.Version))
                                    : "No";

                //更新发布版本号
                Config.Version++;
                Config.LastReleaseTime = DateTime.Now;

                return Content(zipFile);
            }
            catch (Exception ex)
            {
                Infrastructure.Log.Error("PublishController.All", ex.Message + ex.StackTrace);
                return Content("Error");
            }

        }

        /// <summary>
        /// 增量生成
        /// </summary>
        public ActionResult Increment()
        {
            try
            {
                //部署未发布文章
                using (var ab = new ArticleBLL())
                {
                    var list = ab.GetUnReleaseArticles();
                    if (list.Any())
                        ReleaseArticle(list);

                    ReleaseWebIndex();
                }

                //部署广告文件(js&image)
                CreateAd();
                CreateAd(true);

                //压缩打包待发布文件
                var zipFile = h.Zip.ZipDir(Config.Release)
                                  ? Url.Content(string.Format(@"~/Release/{0}v{1}.zip", DateTime.Today.ToString("yyyyMMdd"), Config.Version))
                                  : "No";

                //更新发布版本号
                Config.Version++;
                Config.LastReleaseTime = DateTime.Now;

                return Content(zipFile);
            }
            catch (Exception ex)
            {
                Infrastructure.Log.Error("PublishController.Increment", ex.Message + ex.StackTrace);
                return Content("Error");
            }
        }

        /// <summary>
        /// 生成SiteMap.xml
        /// </summary>
        public ActionResult SiteMap()
        {
            try
            {
                using (var ab = new ArticleBLL())
                {
                    var articles = ab.GetAllArticles().OrderBy(a => a.ContentTemp).ThenBy(a => a.MeunId).ThenBy(a => a.ID);
                    foreach (var article in articles)
                        using (var mb = new MeunBLL())
                        {
                            var menu = mb.Get(article.MeunId ?? 0);
                            article.PageName = string.Format("{0}{1}.html",
                                string.IsNullOrWhiteSpace(menu.ContentDir) ? "" : menu.ContentDir.Trim('/') + "/",
                                string.IsNullOrWhiteSpace(article.PageName) ? article.ID.ToString() : article.PageName);
                        }

                    using (var nv = new h.NVelocityHelper())
                    {
                        nv.Put("list", articles);
                        foreach (var map in GetSiteMapConfig())
                            nv.Deploy(map.Item1, map.Item2);
                    }
                }

                //压缩打包待发布文件
                var zipFile = h.Zip.ZipDir(Config.Release)
                    ? Url.Content(string.Format(@"~/Release/{0}v{1}.zip", DateTime.Today.ToString("yyyyMMdd"), Config.Version))
                    : "No";

                //更新发布版本号
                Config.Version++;
                Config.LastReleaseTime = DateTime.Now;

                return Content(Url.Content(zipFile));
            }
            catch (Exception ex)
            {
                Infrastructure.Log.Error("PublishController.SiteMap", ex.Message + ex.StackTrace);
                return Content("Error");
            }
        }

        /// <summary>
        /// 按栏目生成
        /// </summary>
        public ActionResult ByMenu()
        {
            var bll = new MeunBLL();
            var list = new List<Meun>();
            bll.GetALLSort(0, ref list);
            ViewBag.PagList = list;

            return View();
        }

        /// <summary>
        /// 按栏目生成
        /// </summary>
        [HttpPost]
        public ActionResult ByMenu(int id)
        {
            try
            {
                //发布当前栏目
                using (var ab = new ArticleBLL())
                    ReleaseArticle(ab.GetArticlesByMenuId(id));

                //压缩打包待发布文件
                var zipFile = h.Zip.ZipDir(Config.Release)
                    ? Url.Content(string.Format(@"~/Release/{0}v{1}.zip", DateTime.Today.ToString("yyyyMMdd"), Config.Version))
                    : "No";

                //更新发布版本号
                Config.Version++;
                Config.LastReleaseTime = DateTime.Now;

                return Content(zipFile);
            }
            catch (Exception ex)
            {
                Infrastructure.Log.Error("PublishController.ByMenu", ex.Message + ex.StackTrace);
                return Content("Error");
            }
        }

        /// <summary>
        /// 发布包管理
        /// </summary>
        public ViewResult Package(int index = 0)
        {
            var folder = new DirectoryInfo(Server.MapPath(@"~/Release/"));
            if (folder.Exists)
            {
                var list = folder.GetFiles("*.zip");
                ViewBag.PagList = new Paging<FileInfo>(index, list.AsQueryable().OrderByDescending(f => f.CreationTime));
            }
            else
            {
                ViewBag.PagList = new Paging<FileInfo>(0, new List<FileInfo>().AsQueryable());
            }

            return View();
        }

        /// <summary>
        /// 发布文章
        /// </summary>
        private void ReleaseArticle(IEnumerable<Article> list)
        {
            var menus = new List<int>();
            var mobileMenus = new List<int>();

            foreach (var article in list)
                CreateArticle(article, menus, mobileMenus);

            //生成文章列表
            foreach (var id in menus)
            {
                CreateList(id);
                CreateIndex(id);
            }

            //生成移动端文章列表
            foreach (var id in mobileMenus)
            {
                CreateList(id, true);
                CreateIndex(id, true);
            }
        }

        /// <summary>
        /// 生成文章
        /// </summary>
        private void CreateArticle(Article article, ICollection<int> menus, ICollection<int> mobileMenus)
        {
            using (var mb = new MeunBLL())
            {
                var menu = mb.Get(article.MeunId ?? 0);

                var contentTemp = string.IsNullOrWhiteSpace(article.ContentTemp) ? menu.ContentTemp : article.ContentTemp;
                var dir = string.Format("{0}/{1}", Config.WebRoot.TrimEnd('/'), (menu.ContentDir ?? "").TrimStart('/'));

                CreateHtml(article, menu, contentTemp, dir);
                CopyImage(article.Thumbnail, article.Contents, dir);

                if (!string.IsNullOrWhiteSpace(menu.ListTemp) && article.MeunId.HasValue && !menus.Contains(article.MeunId.Value))
                    menus.Add(article.MeunId.Value);

                //生成移动端页面
                if (!string.IsNullOrWhiteSpace(article.Recommend) && article.Recommend.Contains(",1,"))
                {
                    var mcTemp = string.IsNullOrWhiteSpace(article.MobileTemp) ? menu.MobileContentTemp : article.MobileTemp;
                    var mobileDir = string.Format("{0}/{1}", Config.MobileRoot.TrimEnd('/'), (menu.ContentDir ?? "").TrimStart('/'));

                    CreateHtml(article, menu, mcTemp, mobileDir);
                    CopyImage(article.Thumbnail, article.MobileContents, mobileDir);

                    if (!string.IsNullOrWhiteSpace(menu.MobileListTemp) && article.MeunId.HasValue && !mobileMenus.Contains(article.MeunId.Value))
                        mobileMenus.Add(article.MeunId.Value);
                }
            }
            //更新文章发布状态
            using (var ab = new ArticleBLL())
                ab.Publish(article.ID);
        }

        /// <summary>
        /// 生成列表
        /// </summary>
        private void CreateList(int id, bool isMobile = false)
        {
            using (var ab = new ArticleBLL())
            {
                //过滤到不需要在移动端显示的文章
                var articles = ab.GetArticlesByMenuId(id).WhereIf(a => !string.IsNullOrWhiteSpace(a.Recommend) && a.Recommend.Contains(",1,"), isMobile).ToList();

                foreach (var article in articles)
                    article.PageName = string.Format("{0}.html", string.IsNullOrWhiteSpace(article.PageName)
                                           ? article.ID.ToString()
                                           : article.PageName);

                using (var mb = new MeunBLL())
                {
                    var menu = mb.Get(id);
                    var temp = isMobile ? menu.MobileListTemp : menu.ListTemp;
                    var pageSize = isMobile ? menu.MobileListSize ?? 1 : menu.ListSize ?? 1;
                    var pages = new List<Page>();

                    if (string.IsNullOrWhiteSpace(temp)) return;

                    //构造页码
                    for (int i = 1; i <= (articles.Count - 1) / pageSize + 1; i++)
                        pages.Add(new Page { Index = i, Url = string.Format("{0}{1}.html", menu.ListPrefix ?? "", i) });

                    for (int i = 1; i <= (articles.Count - 1) / pageSize + 1; i++)
                    {
                        var list = articles.Skip((i - 1) * pageSize).Take(pageSize).ToList();

                        using (var nv = new h.NVelocityHelper())
                        {
                            nv.Put("list", list);
                            nv.Put("pages", pages);
                            nv.Put("menu", menu);
                            nv.Put("parents", GetParents(menu.ID));
                            nv.Put("siblings", GetSiblings(menu.ParentId ?? 0));
                            var dir = string.Format("{0}/{1}", isMobile ? Config.MobileRoot.TrimEnd('/') : Config.WebRoot.TrimEnd('/'), (menu.ListDir ?? "").TrimStart('/'));
                            nv.Deploy(temp, string.Format(@"{0}/{1}{2}.html", dir.TrimEnd('/'), menu.ListPrefix ?? "", i));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 生成栏目首页
        /// </summary>
        private void CreateIndex(int id, bool isMobile = false)
        {
            using (var mb = new MeunBLL())
            {
                var menu = mb.Get(id);

                using (var nv = new h.NVelocityHelper())
                {
                    if (isMobile)
                    {
                        //生成该栏目Mobile站点首页
                        if (!string.IsNullOrWhiteSpace(menu.MobileHomeTemp))
                            nv.Deploy(menu.MobileHomeTemp,
                                string.Format(@"{0}/{1}/{2}.html", Config.MobileRoot.TrimEnd('/'),
                                    (menu.HomeDir ?? "").Trim('/'),
                                    string.IsNullOrWhiteSpace(menu.IndexName) ? "Index" : menu.IndexName));
                    }
                    else
                    {
                        //生成该栏目Web站点首页
                        if (!string.IsNullOrWhiteSpace(menu.HomeTemp))
                            nv.Deploy(menu.HomeTemp,
                                string.Format(@"{0}/{1}/{2}.html", Config.WebRoot.TrimEnd('/'),
                                    (menu.HomeDir ?? "").Trim('/'),
                                    string.IsNullOrWhiteSpace(menu.IndexName) ? "Index" : menu.IndexName));
                    }
                }
            }
        }

        /// <summary>
        /// 生成网站首页
        /// </summary>
        private void ReleaseWebIndex()
        {
            var config = GetIndexConfig();
            using (var mb = new MeunBLL())
            {
                var menu = mb.Get(config.Item1);

                if (menu == null) return;

                using (var nv = new h.NVelocityHelper())
                {
                    if (!string.IsNullOrWhiteSpace(menu.HomeTemp))
                    {
                        foreach (var item in config.Item2)
                        {
                            using (var ab = new ArticleBLL())
                            {
                                var list = ab.GetArticlesByMenuId(item.Item2, item.Item3).ToList();
                                foreach (var article in list)
                                {
                                    var m = mb.Get(article.MeunId ?? 0);
                                    article.PageName = string.Format("{0}/{1}.html", (m.ContentDir ?? "").Trim('/'), string.IsNullOrWhiteSpace(article.PageName)
                                                                                                                                                                           ? article.ID.ToString()
                                                                                                                                                                           : article.PageName);
                                }

                                nv.Put(item.Item1, list);
                            }
                        }
                        nv.Put("menu", menu);
                        nv.Deploy(menu.HomeTemp, string.Format(@"{0}/{1}/{2}.html", Config.WebRoot.TrimEnd('/'),
                                                                                                                                (menu.HomeDir ?? "").Trim('/'),
                                                                                                                                string.IsNullOrWhiteSpace(menu.IndexName) ? "Index" : menu.IndexName));
                    }

                    if (!string.IsNullOrWhiteSpace(menu.MobileHomeTemp))
                    {
                        foreach (var item in config.Item2)
                        {
                            using (var ab = new ArticleBLL())
                            {
                                var list = ab.GetArticlesByMenuId(item.Item2, item.Item3).Where(a => !string.IsNullOrWhiteSpace(a.Recommend) && a.Recommend.Contains(",1,")).ToList();
                                foreach (var article in list)
                                {
                                    var m = mb.Get(article.MeunId ?? 0);
                                    article.PageName = string.Format("{0}/{1}.html", (m.ContentDir ?? "").Trim('/'), string.IsNullOrWhiteSpace(article.PageName)
                                                                                                                                                                               ? article.ID.ToString()
                                                                                                                                                                               : article.PageName);
                                }

                                nv.Put(item.Item1, list);
                            }
                        }
                        nv.Put("menu", menu);
                        nv.Deploy(menu.MobileHomeTemp, string.Format(@"{0}/{1}/{2}.html", Config.MobileRoot.TrimEnd('/'),
                                                                                                                                            (menu.HomeDir ?? "").Trim('/'),
                                                                                                                                            string.IsNullOrWhiteSpace(menu.IndexName) ? "Index" : menu.IndexName));
                    }

                }
            }
        }

        /// <summary>
        /// 获取站点首页生成配置
        /// </summary>
        private Tuple<int, List<Tuple<string, string[], int>>> GetIndexConfig()
        {
            var xd = XDocument.Load(Server.MapPath("~/Index.Config.xml"));
            var xe = xd.Element("Index");
            if (xe == null) return null;

            var lists = xe.Elements("List").Select(e => new Tuple<string, string[], int>(e.Attribute("Name").Value, e.Attribute("Menus").Value.Split(','), int.Parse(e.Attribute("Count").Value))).ToList();
            return new Tuple<int, List<Tuple<string, string[], int>>>(int.Parse(xe.Attribute("MenuId").Value), lists);
        }

        /// <summary>
        /// 获取Sitemap生成配置
        /// </summary>
        private IEnumerable<Tuple<string, string>> GetSiteMapConfig()
        {
            var xd = XDocument.Load(Server.MapPath("~/SiteMap.Config.xml"));
            var xe = xd.Element("Maps");
            return xe == null ? null : xe.Elements("Map").Select(e => new Tuple<string, string>(e.Attribute("Temp").Value, e.Attribute("Dic").Value)).ToList();
        }

        /// <summary>
        /// 生成Html页面
        /// </summary>
        private void CreateHtml(Article article, Meun menu, string template, string dir)
        {
            if (string.IsNullOrWhiteSpace(template)) return;

            using (var nv = new h.NVelocityHelper())
            {
                nv.Put("model", article);
                nv.Put("menu", menu);
                nv.Put("parents", GetParents(menu.ID));
                nv.Put("siblings", GetSiblings(menu.ParentId ?? 0));
                var name = string.IsNullOrWhiteSpace(article.PageName) ? article.ID.ToString() : article.PageName;
                nv.Deploy(template, string.Format(@"{0}/{1}.html", dir.TrimEnd('/'), name));
            }
        }

        /// <summary>
        /// 复制文章相关图片
        /// </summary>
        private void CopyImage(string thumbnail, string content, string directory)
        {
            const string upload = @"~/Upload/";
            const string regImg = @"<img[^>]*src\s*=\s*['""]([\w/\-\.]*)['""][^>]*";
            var preview = string.Format("{0}/{1}/", Config.Preview.TrimEnd('/'), directory.TrimStart('/').TrimEnd('/'));
            var release = string.Format("{0}/{1}/", Config.Release.TrimEnd('/'), directory.TrimStart('/').TrimEnd('/'));

            //生成图片目录
            if (!Directory.Exists(preview + "img"))
                Directory.CreateDirectory(preview + "img");
            if (!Directory.Exists(release + "img"))
                Directory.CreateDirectory(release + "img");

            //缩略图
            if (!string.IsNullOrWhiteSpace(thumbnail))
            {
                var fileName = Path.GetFileName(thumbnail);
                var path = Server.MapPath(upload + fileName);
                System.IO.File.Copy(path, preview + thumbnail, true);
                System.IO.File.Copy(path, release + thumbnail, true);
            }

            //内容中图片
            if (string.IsNullOrWhiteSpace(content)) return;
            foreach (Match match in Regex.Matches(content, regImg, RegexOptions.IgnoreCase))
            {
                var fileName = Path.GetFileName(match.Groups[1].Value);
                var path = Server.MapPath(upload + fileName);
                if (!System.IO.File.Exists(path)) continue;
                System.IO.File.Copy(path, preview + match.Groups[1].Value, true);
                System.IO.File.Copy(path, release + match.Groups[1].Value, true);
            }
        }

        /// <summary>
        /// 生成广告文件
        /// </summary>
        private void CreateAd(bool isMobile = false, bool isIncrement = true)
        {
            var root = (isMobile ? Config.MobileRoot : Config.WebRoot).Trim('/');

            using (var ab = new AdBLL())
            {
                var ads = isIncrement ? ab.SelectNoReleaseAds() : ab.Select();

                foreach (var item in ads)
                {
                    var previewPath = string.Format("{0}/{1}/js/ad/{2}", Config.Preview.TrimEnd('/'), root, item.FileName ?? "ad.js");
                    var releasePath = string.Format("{0}/{1}/js/ad/{2}", Config.Release.TrimEnd('/'), root, item.FileName ?? "ad.js");
                    var pDir = Path.GetDirectoryName(previewPath);
                    var rDir = Path.GetDirectoryName(releasePath);

                    if (pDir != null && !Directory.Exists(pDir))
                        Directory.CreateDirectory(pDir);
                    if (rDir != null && !Directory.Exists(rDir))
                        Directory.CreateDirectory(rDir);

                    System.IO.File.WriteAllText(previewPath, item.Content, System.Text.Encoding.UTF8);
                    System.IO.File.WriteAllText(releasePath, item.Content, System.Text.Encoding.UTF8);

                    ab.Publish(item.Id);
                }
            }

            //广告图片复制
            var previewAdImgDir = string.Format("{0}/{1}/js/ad/img", Config.Preview.TrimEnd('/'), root);
            var releaseAdImgDir = string.Format("{0}/{1}/js/ad/img", Config.Release.TrimEnd('/'), root);
            var adDir = Server.MapPath("~/Upload/Ad/");

            if (!Directory.Exists(previewAdImgDir))
                Directory.CreateDirectory(previewAdImgDir);
            if (!Directory.Exists(releaseAdImgDir))
                Directory.CreateDirectory(releaseAdImgDir);
            if (!Directory.Exists(adDir))
                Directory.CreateDirectory(adDir);

            CopyFile(adDir, previewAdImgDir);
            CopyFile(adDir, releaseAdImgDir);
        }

        /// <summary>
        /// 复制文件
        /// </summary>
        /// <param name="sources">源路径</param>
        /// <param name="dest">新路径</param>
        private void CopyFile(string sources, string dest)
        {
            var dinfo = new DirectoryInfo(sources);
            //注，这里面传的是路径，并不是文件，所以不能保含带后缀的文件                
            foreach (var f in dinfo.GetFileSystemInfos())
            {
                //目标路径destName = 目标文件夹路径 + 原文件夹下的子文件(或文件夹)名字                
                //Path.Combine(string a ,string b) 为合并两个字符串                     
                var destName = Path.Combine(dest, f.Name);
                if (f is FileInfo)
                {
                    //如果是文件就复制       
                    System.IO.File.Copy(f.FullName, destName, true);//true代表可以覆盖同名文件                     
                }
                else
                {
                    //如果是文件夹就创建文件夹然后复制然后递归复制              
                    Directory.CreateDirectory(destName);
                    CopyFile(f.FullName, destName);
                }
            }
        }


        /// <summary>
        /// 获取同级栏目
        /// </summary>
        /// <param name="parentId"></param>
        /// <returns></returns>
        private List<Meun> GetSiblings(int parentId)
        {
            using (var mb = new MeunBLL())
            {
                return mb.GetSonMeuns(parentId).OrderByDescending(m => m.Sort).ToList();
            }
        }

        /// <summary>
        /// 获取父级栏目
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        private List<Meun> GetParents(int menuId)
        {
            var parents = new List<Meun>();
            using (var mb = new MeunBLL())
            {
                mb.GetParentList(menuId, ref parents);
            }

            return parents;
        }
    }
}
